#include <stdio.h>
#include "SPI_Dev.h"

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "driver/spi_master.h"
#include "driver/gpio.h"
#include <string.h>
#include "font.h"

#include "freertos/queue.h"  

#define ENDEBUG 0
#if ENDEBUG
#define debug(...) printf(__VA_ARGS__)
#else
#define debug(...)
#endif

// To speed up transfers, every SPI transfer sends a bunch of lines. This define specifies how many. More means more memory use,
// but less overhead for setting up / finishing transfers. Make sure 240 is dividable by this.
#define PARALLEL_LINES 16

#define ROW  240		//垂直分辨率 gate   
#define COL  320 		//水平分辨率 source

// static uint8_t spi_dis_cache[170*2]  = {0};

#define X_OFFSET 0 //x轴显示偏移
#define Y_OFFSET 0

#define CS_ENABLE 1
#if CS_ENABLE
#define CS_HIGH     gpio_set_level(LCD_PIN_CS, 1)
#define CS_LOW      gpio_set_level(LCD_PIN_CS, 0)
#else
#define CS_HIGH     
#define CS_LOW      
#endif

#define RS_HIGH     gpio_set_level(LCD_PIN_RS, 1);
#define RS_LOW      gpio_set_level(LCD_PIN_RS, 0);

#define SPI_RATE 40 //Mhz
#define DMA_LEN 4096

//常用的颜色数据
#define RED  		0xf800
#define GREEN		0x07e0
#define BLUE 		0x001f
#define WHITE		0xffff
#define BLACK		0x0000
#define YELLOW      0xFFE0
#define PURPLE_APPROX 0x800F // 这是一种尝试接近紫色的表示，但可能不够准确  
// 一个更常用的、视觉上更接近紫色的表示可能是：  
#define PURPLE_BETTER_APPROX 0xA71F // 这是一个在红色和蓝色之间平衡的尝试
spi_device_handle_t spi_handle;

void spi_bus_init(void)
{
    esp_err_t ret;
    spi_bus_config_t buscfg = {
        .miso_io_num = LCD_PIN_MISO,
        .mosi_io_num = LCD_PIN_MOSI,
        .sclk_io_num = LCD_PIN_CLK,
        .quadwp_io_num = -1,
        .quadhd_io_num = -1,
        .max_transfer_sz = DMA_LEN};//PARALLEL_LINES * 240 * 2 + 8  25600
    spi_device_interface_config_t devcfg = {
        .clock_speed_hz = SPI_RATE * 1000 * 1000,       // Clock out at 10 MHz
        .mode = 0,                               // SPI mode 0
        .spics_io_num = -1,              // CS pin
        .queue_size = 7,                         // We want to be able to queue 7 transactions at a time
        // .pre_cb = NULL,
        .pre_cb = NULL, // Specify pre-transfer callback to handle D/C line
    };
    // Initialize the SPI bus
    ret = spi_bus_initialize(SPI2_HOST, &buscfg, SPI_DMA_CH_AUTO);
    ESP_ERROR_CHECK(ret);
    // Attach the LCD to the SPI bus
    ret = spi_bus_add_device(SPI2_HOST, &devcfg, &spi_handle);
    ESP_ERROR_CHECK(ret);


}

int SPI_transmitBlockData(const uint8_t *data, uint16_t length)
{
    spi_transaction_t t;
	esp_err_t ret;
    memset(&t, 0, sizeof(t)); // Zero out the transaction
    t.length = 8 * length;    // Command is 8 bits
    t.tx_buffer = data;       // The data is the cmd itself
	ret = spi_device_acquire_bus(spi_handle,portMAX_DELAY);
	if (ret==ESP_OK)
	{
		spi_device_polling_transmit(spi_handle, &t); // 轮询方式发送数据
		spi_device_release_bus(spi_handle);
		return 0;
	}else
	{
		return -1;
	}
}

void spi_wr_cmd(uint8_t cmd)
{
    CS_LOW;
    RS_LOW;
    SPI_transmitBlockData(&cmd,1);
    CS_HIGH;
}

void spi_wr_byte(uint8_t data)
{
    CS_LOW;
    RS_HIGH;
    SPI_transmitBlockData(&data,1);
    CS_HIGH;
}

void spi_wr_buffer(uint8_t *data, uint16_t len)
{
    CS_LOW;
    RS_HIGH;
    SPI_transmitBlockData(data,len);
    CS_HIGH;
}

void BlockWrite(uint16_t x1,uint16_t y1, uint16_t x2,uint16_t y2) 
{

	x1 += X_OFFSET; 
    x2 += X_OFFSET;
    y1 += Y_OFFSET;
    y2 += Y_OFFSET;

	spi_wr_cmd(0x2A);
	spi_wr_byte(x1>>8);
	spi_wr_byte(x1);
	spi_wr_byte(x2>>8);
	spi_wr_byte(x2);

	spi_wr_cmd(0x2B);
	spi_wr_byte(y1>>8);
	spi_wr_byte(y1);
	spi_wr_byte(y2>>8);
	spi_wr_byte(y2);
	
	spi_wr_cmd(0x2c);
}


void DispColor_set(uint8_t x,uint8_t y,uint16_t width,uint16_t height, uint16_t color)	
{
	unsigned int i;
	BlockWrite(x,y,width-1,height-1);
    uint32_t xxx = width*height;
	uint8_t data[2] = {(color >> 8),color & 0xFF};
	CS_LOW;
	RS_HIGH;
	for(i=0;i < xxx;i++)   
	{ 
        SPI_transmitBlockData(data,2);			
	}
	CS_HIGH;
}

//指定位置刷新
void lcd_point_refresh(uint16_t x1,uint16_t y1,uint16_t x2 ,uint16_t y2, unsigned char *p)
{
    uint32_t buf_len = (x2-x1+1)*(y2-y1+1)*2;
	int ret;
    BlockWrite(x1,y1,x2,y2);
    CS_LOW;
    RS_HIGH;
    uint32_t i=0;
    uint32_t send_cnt 	= buf_len/DMA_LEN;//需要整行刷新多少行
	uint32_t send_cnt2	= buf_len%DMA_LEN;//整行刷不完还剩多少字节

	if(buf_len==0)
	{
		return;
	}
	for(;i<send_cnt;i++){
		ret = SPI_transmitBlockData(p+i*DMA_LEN,DMA_LEN);
		if (ret !=0)
		{
			return;
		}
	}
	if(send_cnt2!=0){
		SPI_transmitBlockData(p+i*DMA_LEN,send_cnt2);
	}
    CS_HIGH;
}

//高效率低内存方法清空全屏
void lcd_clear(void)
{
	unsigned int i;
	BlockWrite(0,0,ROW-1,COL-1);
	uint8_t *ptr = malloc(COL);
	if (ptr == NULL) {  
        debug("lcd_clear malloc error\n");// 内存分配失败的处理  
        return;
    }  
	memset(ptr,0,COL);
	
	CS_LOW;
	RS_HIGH;
	for(i=0;i < ROW*2;i++)   
	{ 
        SPI_transmitBlockData(ptr,COL);			
	}
	CS_HIGH;
	free(ptr); 
}

//针对16x16的显示函数 lcd会递增 取模软件设置逐行式
void lcd_show_character(uint8_t  x,uint8_t y,const uint8_t *font,uint16_t fore_color,uint16_t back_color)
{
	uint8_t m,temp,i;
	uint16_t arr_length = 32;//16x16 一共需要32个8位   16*16/8
	uint8_t fore_color_buf[2] = {fore_color>>8,fore_color&0x00ff};
	uint8_t back_color_buf[2] = {back_color>>8,back_color&0x00ff};
	BlockWrite(x,y,x+15,y+15);
    CS_LOW;
	RS_HIGH;	
	for (i = 0; i < arr_length; i++)
	{
		temp = font[i];
		for (m = 0; m < 8; m++) //逐行式 一个八位递增
		{
			if (temp&0x01)
			{
				SPI_transmitBlockData(fore_color_buf,2);//前景色
			}
			else
			{
				SPI_transmitBlockData(back_color_buf,2);//背景色
			}
			temp >>= 1;
		}
	}
	CS_HIGH;
}

//针对8x16的显示函数 lcd会递增 取模软件设置逐行式
void lcd_show_char16(uint16_t  x,uint16_t y,uint8_t font,uint16_t fore_color,uint16_t back_color)
{
	if (y>COL || x>ROW)
	{
		return;
	}
	uint8_t font_num = font-32;//ascll码从32 空格开始
	uint8_t char_x = 8-1;//字体宽度8s
	uint8_t m,temp,i;
	uint16_t arr_length = 16;//字体高度  16x16 一共需要32个8位   16*16/8
	uint8_t fore_color_buf[2] = {fore_color>>8,fore_color&0x00ff};
	uint8_t back_color_buf[2] = {back_color>>8,back_color&0x00ff};
	int ret;
	BlockWrite(x,y,x+char_x,y+15);
    CS_LOW;
	RS_HIGH;	
	for (i = 0; i < arr_length; i++)
	{
		temp = ascii_1608[font_num][i];
		for (m = 0; m < 8; m++) //逐行式 一个八位递增
		{
			if (temp&0x01)
			{
				ret = SPI_transmitBlockData(fore_color_buf,2);//前景色
				if(ret != 0)
				return;
			}
			else
			{
				ret = SPI_transmitBlockData(back_color_buf,2);//背景色
				if(ret != 0)
				return;
			}
			temp >>= 1;
		}
	}
	CS_HIGH;
}


void lcd_show_string16(uint16_t  x,uint16_t y, uint8_t *font,uint8_t font_num,uint16_t fore_color,uint16_t back_color)
{
	for (uint8_t i = 0; i < font_num; i++)
	{
		lcd_show_char16(x+8*i,y,font[i],fore_color,back_color);
	}
}

void lcd_show_num16_one(uint8_t x,uint16_t y,uint16_t num,uint16_t fore_color,uint16_t back_color)
{
	uint8_t m,temp,i;
	uint8_t char_x = 7;
	uint8_t fore_color_buf[2] = {fore_color>>8,fore_color&0x00ff};
	uint8_t back_color_buf[2] = {back_color>>8,back_color&0x00ff};
	int ret ;
	BlockWrite(x,y,x+char_x,y+15);
    CS_LOW;
	RS_HIGH;	
	for (i = 0; i < 16; i++)
	{
		temp = ascii_1608[16+num][i];
		for (m = 0; m < 8; m++) //逐行式 一个八位递增
		{
			if (temp&0x01)
			{
				ret = SPI_transmitBlockData(fore_color_buf,2);//前景色
				if(ret != 0)
				return;
			}
			else
			{
				ret = SPI_transmitBlockData(back_color_buf,2);//背景色
				if(ret != 0)
				return;
			}
			temp >>= 1;
		}
	}
	CS_HIGH;
}


void lcd_show_num16_2(uint8_t x,uint16_t y,uint16_t num,uint16_t fore_color,uint16_t back_color)
{
	lcd_show_num16_one(x,y,(num/10)%10,fore_color,back_color);
	lcd_show_num16_one(x+8,y,num%10,fore_color,back_color);
}

void lcd_show_num_uint(uint8_t x,uint16_t y,uint32_t num,uint16_t fore_color,uint16_t back_color)
{
    uint32_t temp = num;  
    uint8_t int_digits = 0;
    // 计算整数部分的位数  
    while (temp > 0) {  
        temp /= 10;  
        int_digits++;  
    }

	// 如果整数部分为0，则至少显示一个'0'  
    if (int_digits == 0) {  
        lcd_show_num16_one(x, y,0, fore_color, back_color);  
        int_digits = 1; // 更新位数以便显示小数点  
    } 

    // 显示整数部分，从最高位开始  
    temp = num;  
    for (int i = int_digits - 1; i >= 0; i--) {  
        uint8_t digit = temp % 10; // 直接取余数得到当前位的数字  
        lcd_show_num16_one(x + 8 * i, y, digit, fore_color, back_color); // 转换为字符并显示  
        temp /= 10; // 为下一次迭代准备  
    }
}


